﻿#include "stdafx.h"
#if _QG_USE_SDL
#include "qg/qgasv.h"
#include "extend/sbvalues.h"

#ifdef _MSC_VER
#	if _SB_64_
#		pragma comment(lib, "SDL64")
#	else
#		pragma comment(lib, "SDL")
#	endif
#endif

//////////////////////////////////////////////////////////////////////////
// SDL 스텁
namespace _sdl
{
	class SDLStub;

	// 키매핑
	static kbyte KeyMap[SDLK_LAST] = {0, };

	// 인스턴스
	static SDLStub*	Instance = NULL;


	//////////////////////////////////////////////////////////////////////////
	// 본론
	class SDLStub : public qgStub
	{
		KCH_AUTOCLASS(SDLStub, qgStub, "RenderStubSDL");

	private:
		SDL_Surface*		_screen;
		Uint32				_sdl_flags;
		bool				_sdl_ok;
		SDL_SysWMinfo		_sdl_info;

		double				_active;

		qgColorFormat		_fmt;

	private:
		static kvoid TypeInitialize(kType type)
		{
			// LBUTTON
			// RBUTTON
			// CANCEL
			// MBUTTON
			// XPBUTTON
			// XNBUTTON
			KeyMap[SDLK_BACKSPACE] = QIK_BACK;
			KeyMap[SDLK_TAB] = QIK_TAB;
			KeyMap[SDLK_CLEAR] = QIK_CLEAR;
			KeyMap[SDLK_RETURN] = QIK_RETURN;
			// SHIFT
			// CTRL
			// ALT
			KeyMap[SDLK_PAUSE] = QIK_PAUSE;
			KeyMap[SDLK_CAPSLOCK] = QIK_CAPSLOCK;
			// HANGUL
			// JUNJA
			// HANJA
			KeyMap[SDLK_ESCAPE] = QIK_ESCAPE;
			// CONVERT
			// NONCONVERT
			// ACCEPT
			// MODECHANGE
			KeyMap[SDLK_SPACE] = QIK_SPACE;
			KeyMap[SDLK_PAGEUP] = QIK_PGUP;
			KeyMap[SDLK_PAGEDOWN] = QIK_PGDN;
			KeyMap[SDLK_END] = QIK_END;
			KeyMap[SDLK_HOME] = QIK_HOME;
			KeyMap[SDLK_LEFT] = QIK_LEFT;
			KeyMap[SDLK_UP] = QIK_UP;
			KeyMap[SDLK_RIGHT] = QIK_RIGHT;
			KeyMap[SDLK_DOWN] = QIK_DOWN;
			// SELECT
			KeyMap[SDLK_PRINT] = QIK_PRINT;
			// EXECUTE
			KeyMap[SDLK_INSERT] = QIK_INSERT;
			KeyMap[SDLK_DELETE] = QIK_DELETE;
			KeyMap[SDLK_HELP] = QIK_HELP;
			KeyMap[SDLK_0] = QIK_0;
			KeyMap[SDLK_1] = QIK_1;
			KeyMap[SDLK_2] = QIK_2;
			KeyMap[SDLK_3] = QIK_3;
			KeyMap[SDLK_4] = QIK_4;
			KeyMap[SDLK_5] = QIK_5;
			KeyMap[SDLK_6] = QIK_6;
			KeyMap[SDLK_7] = QIK_7;
			KeyMap[SDLK_8] = QIK_8;
			KeyMap[SDLK_9] = QIK_9;
			KeyMap[SDLK_a] = QIK_A;
			KeyMap[SDLK_b] = QIK_B;
			KeyMap[SDLK_c] = QIK_C;
			KeyMap[SDLK_d] = QIK_D;
			KeyMap[SDLK_e] = QIK_E;
			KeyMap[SDLK_f] = QIK_F;
			KeyMap[SDLK_g] = QIK_G;
			KeyMap[SDLK_h] = QIK_H;
			KeyMap[SDLK_i] = QIK_I;
			KeyMap[SDLK_j] = QIK_J;
			KeyMap[SDLK_k] = QIK_K;
			KeyMap[SDLK_l] = QIK_L;
			KeyMap[SDLK_m] = QIK_M;
			KeyMap[SDLK_n] = QIK_N;
			KeyMap[SDLK_o] = QIK_O;
			KeyMap[SDLK_p] = QIK_P;
			KeyMap[SDLK_q] = QIK_Q;
			KeyMap[SDLK_r] = QIK_R;
			KeyMap[SDLK_s] = QIK_S;
			KeyMap[SDLK_t] = QIK_T;
			KeyMap[SDLK_u] = QIK_U;
			KeyMap[SDLK_v] = QIK_V;
			KeyMap[SDLK_w] = QIK_W;
			KeyMap[SDLK_x] = QIK_X;
			KeyMap[SDLK_y] = QIK_Y;
			KeyMap[SDLK_z] = QIK_Z;
			KeyMap[SDLK_LSUPER] = QIK_LWIN;
			KeyMap[SDLK_RSUPER] = QIK_RWIN;
			// APPS
			KeyMap[SDLK_POWER] = QIK_SLEEP;
			KeyMap[SDLK_KP0] = QIK_NUMPAD0;
			KeyMap[SDLK_KP1] = QIK_NUMPAD1;
			KeyMap[SDLK_KP2] = QIK_NUMPAD2;
			KeyMap[SDLK_KP3] = QIK_NUMPAD3;
			KeyMap[SDLK_KP4] = QIK_NUMPAD4;
			KeyMap[SDLK_KP5] = QIK_NUMPAD5;
			KeyMap[SDLK_KP6] = QIK_NUMPAD6;
			KeyMap[SDLK_KP7] = QIK_NUMPAD7;
			KeyMap[SDLK_KP8] = QIK_NUMPAD8;
			KeyMap[SDLK_KP9] = QIK_NUMPAD9;
			KeyMap[SDLK_KP_MULTIPLY] = QIK_MULTIPLY;
			KeyMap[SDLK_KP_PLUS] = QIK_ADD;
			// SEPARATOR
			KeyMap[SDLK_KP_MINUS] = QIK_SUBTRACT;
			KeyMap[SDLK_KP_PERIOD] = QIK_DECIMAL;
			KeyMap[SDLK_KP_DIVIDE] = QIK_DIVIDE;
			KeyMap[SDLK_F1] = QIK_F1;
			KeyMap[SDLK_F2] = QIK_F2;
			KeyMap[SDLK_F3] = QIK_F3;
			KeyMap[SDLK_F4] = QIK_F4;
			KeyMap[SDLK_F5] = QIK_F5;
			KeyMap[SDLK_F6] = QIK_F6;
			KeyMap[SDLK_F7] = QIK_F7;
			KeyMap[SDLK_F8] = QIK_F8;
			KeyMap[SDLK_F9] = QIK_F9;
			KeyMap[SDLK_F10] = QIK_F10;
			KeyMap[SDLK_F11] = QIK_F11;
			KeyMap[SDLK_F12] = QIK_F12;
			KeyMap[SDLK_F13] = QIK_F13;
			KeyMap[SDLK_F14] = QIK_F14;
			KeyMap[SDLK_F15] = QIK_F15;
			// F16 ~ F24
			KeyMap[SDLK_NUMLOCK] = QIK_NUMLOCK;
			KeyMap[SDLK_SCROLLOCK] = QIK_SCROLL;
			KeyMap[SDLK_LSHIFT] = QIK_LSHIFT;
			KeyMap[SDLK_RSHIFT] = QIK_RSHIFT;
			KeyMap[SDLK_LCTRL] = QIK_LCTRL;
			KeyMap[SDLK_RCTRL] = QIK_RCTRL;
			KeyMap[SDLK_LALT] = QIK_LALT;
			KeyMap[SDLK_RALT] = QIK_RALT;
			KeyMap[SDLK_COMMA] = QIK_COMMA;
			KeyMap[SDLK_PLUS] = QIK_PLUS;
			KeyMap[SDLK_MINUS] = QIK_MINUS;
			KeyMap[SDLK_PERIOD] = QIK_PERIOD;
			// 그 아래 없음
		}

		static kvoid TypeFinalize(kType type)
		{
		}

	public:
		SDLStub()
			: super_type()
		{
			_active = k_timer_get_abs(_timer);

			// 다중 인스턴스 금지
			if (Instance)
			{
				// SDL은 다중 인스턴스 못 만든다
				kobj_tracep(KLV_CRITICAL, KERR_NOMULTI);
				return;
			}

			Instance = this;

			//
			Uint32 iflags = SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;

			if (false)
			{
				// 조이스틱 지원하려면 추가
				iflags = SDL_INIT_JOYSTICK;
			}

			if (SDL_Init(iflags) < 0)
			{
				kobj_tracef(KLV_ERROR, "unable to initialize SDL = %s", SDL_GetError());
				return;
			}

			SDL_EnableUNICODE(1);
			SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

			SDL_VERSION(&_sdl_info.version);
			SDL_GetWMInfo(&_sdl_info);

			//
#if _SB_UNIX_
			_handle = (kpointer)_sdl_info.info.x11.window;
#else
			_handle = _sdl_info.window;
#endif

			_sdl_ok = true;
		}

		~SDLStub()
		{
			// 다중 인스턴스가 안된다...
			SDL_Quit();

			if (Instance == this)
				Instance = NULL;
		}

		//
		kbool Create(const tpl::Rect& area, kint flags)
		{
			if (!_sdl_ok)
			{
				// 생성자 검사 통과에 실패함
				return false;
			}

			//
			int bpp, width = area.Width(), height = area.Height();
			Uint32 sf = 0;
			bool usegl = false;

			if (K_OFLAG(flags, QGSDF_FULL))
				sf |= SDL_FULLSCREEN;
			if (K_OFLAG(flags, QGSDF_LAYOUT))
				sf |= SDL_RESIZABLE;
			if (K_OFLAG(flags, QGSDF_INDP))
			{
				sf |= SDL_OPENGL | SDL_DOUBLEBUF;
				usegl = true;
			}

			// 크기 보관
			_size.Set(rt.right - rt.left, rt.bottom - rt.top);

			// OPENGL 특별 취급
			if (usegl)
			{
				bpp = K_OFLAG(flags, QGSDF_BGRA) ? 32 : K_OFLAG(flags, QGSDF_DITHER) ? 16 : 32;

				if (bpp == 16)
				{
					SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 4);
					SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 4);
					SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 4);
					SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 1);
					SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
				}
				else
				{
					SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
					SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
				}

				SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
				//SDL_GL_SetAttribute(SDL_GL_STEREO, 1);			// 3D 스테레오...는 할 준비가 안됨
				SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);

#if SDL_VERSION_ATLEAST(1,3,0) 
				SDL_GL_SetSwapInterval(0);
#else
				SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
#endif

#if _SB_WINDOWS_
				// 왜 윈도우 밖에 안되는 거시여!!!
				if (K_OFLAG(flags, QGSDF_MSAA))
				{
					SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
					SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);	// 샘플 개수는 바꿀 수 있게 해야함 2, 4, 6, 8
				}
#endif

				_screen = SDL_SetVideoMode(width, height, bpp, sf);
			}
			else
			{
				bpp = K_OFLAG(flags, QGSDF_BGRA) ? 32 : 0;
				_screen = SDL_SetVideoMode(width, height, bpp, sf);
			}

			if (!_screen && usegl)
			{
				SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
				sf &= ~SDL_DOUBLEBUF;
				_screen = SDL_SetVideoMode(width, height, bpp, sf);

				// 디바이스 만들 때 더블 버퍼 안썼다고 알려줄 것
				K_UFLAG(&_insts, QGSDI_EXF_1, true);

#if _SB_WINDOWS_
				// MSAA도 잘 안되드라...
				if (K_OFLAG(flags, QGSDF_MSAA))
					SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
#endif
			}

			if (!_screen)
			{
				kobj_tracep(KLV_ERROR, KERR_NOSUPP);
				return false;
			}

			SDL_WM_SetCaption(_text, _text);

#if _SB_DEBUG_ && _SB_WINDOWS_
			char cname[64];
			k_getcomputername(cname, 64 - 1);

			tpl::Point rp(0, 0);
			tpl::Point rs(width, height);
			tpl::Point ss(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));

			if (k_streqv(cname, "KSHDT"))
			{
				rp.x = ss.x - rs.x - 10;
				rp.y = ss.y - rs.y - 200;
			}
			else
			{
				rp.x = 0;
				rp.y = ss.y - rs.y - 100;
			}

			SetWindowPos((HWND)_handle, NULL, rp.x, rp.y, 0, 0, SWP_NOREPOSITION | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
#endif

			//
			if (_screen->format->BitsPerPixel == 16)
				_fmt = _screen->format->Amask != 0 ? QGCF16_RGBA : QGCF16_RGB;
			else
			{
				_fmt =
					flags&QGSDF_BGRA ? QGCF32_BGRA :
					_screen->format->Amask != 0 ? QGCF32_RGBA : QGCF32_RGB;
			}

			// 영역 저장, 정확한 스페이스는 알 수 없다
			_bound.Set(0, 0, _screen->w, _screen->h);
			_sysbnd.Set(0, 0, _screen->w, _screen->h);

			// 전체 화면이 아니라면 크기 저장 -> SDL은 별 의미 없긴 할 건데...
			if (!K_OFLAG(flags, QGSDF_FULL))
				_size.Set(_screen->w, _screen->h);

			// 설정 저장
			_flags = flags;
			_insts = QGSDI_ACTIVE;

			_sdl_flags = sf;

			kobj_tracef(KLV_INFO, "SDL STUB using version %d.%d.%d",
				_sdl_info.version.major, _sdl_info.version.minor, _sdl_info.version.patch);

			return true;
		}

		//
		K_OVR kbool Close()
		{
			K_UFLAG(&_insts, QGSDI_EXIT, true);

			return true;
		}

		//
		K_OVR kbool Layout()
		{
#if 0
			// 일단, 이 함수는 자동 레이아웃인데.... SDL에서 방법을 모르겠구나
			tpl::Point prev(_size);

			// 이 부분을 어케 하냐...
			_sysbnd.Set(0, 0, ev.resize.w, ev.resize.h);
			_bound.Set(0, 0, ev.resize.w, ev.resize.h);
			_size.Set(ev.resize.w, ev.resize.h);

			// 이벤트 트리거
			if (prev.x != _size.x || prev.y != _size.y)
			{
				_screen = SDL_SetVideoMode(_size.x, _size.y, 0, _sdl_flags);

				if (CanInvokeHandler(QGSEV_LAYOUT))
				{
					qgSevLayout e = {_size.x, _size.y, false};	// 최대화 상태를 알 수 없음
					UnsafeInvokeHandler(QGSEV_LAYOUT, &e);
				}
			}
#endif

			return true;
		}

		//
		K_OVR kbool Poll()
		{
			//
			SDL_Event ev;

			while (SDL_PollEvent(&ev))
			{
				if (K_OFLAG(_insts, QGSDI_EXIT))
					return true;

				MesgProc(ev);
			}

			// 조이스틱

			//
			_polls++;

			return FALSE;
		}

		//
		K_OVR kbool GetControllerInput(kint slot, qgUimControllerInput* inf)
		{
			k_return_value_if_fail((ksize_t)slot < 4, false);
			k_return_value_if_fail(inf, false);

			return false;
		}

		//
		K_OVR kbool GetControllerInfo(kint slot, qgUimControllerInfo* stt)
		{
			k_return_value_if_fail((ksize_t)slot < 4, false);
			k_return_value_if_fail(stt, false);

			return false;
		}

		//
		K_OVR kbool SetControllerEnable(kbool v)
		{
			return false;
		}

		//
		K_OVR kbool SetControllerVibration(kint slot, const qgUimControllerVibration& v)
		{
			k_return_value_if_fail((ksize_t)slot < 4, false);

			return false;
		}

		//
		K_OVR kvoid SetDropEnable(kbool v)
		{
		}

		//
		K_OVR kvoid SetAcsEnable(kbool v)
		{
		}

		//
		K_OVR kvoid SetImeEnable(kbool v)
		{
		}

		//
		K_OVR kbool SetText(const char* buffer)
		{
			k_return_value_if_fail(buffer, false);

			if (*buffer)
			{
				k_strncpy(_text, buffer, 260 - 1);

				char sz[260];
				k_conv_utf8_to_asc(sz, 260 - 1, _text, 0);
				SDL_WM_SetCaption(sz, sz);
			}
			else
			{
				_text[0] = '\0';
				SDL_WM_SetCaption("", "");
			}

			return true;
		}

		//
		K_OVR kvoid HoldMouse(kbool v)
		{
		}

		//
		K_OVR kintptr SystemProc(const qgSevSystem& e)
		{
			SDL_Event* ev = (SDL_Event*)e.wparam;
			MesgProc(*ev);
			return 0;
		}

		//
		void MesgProc(const SDL_Event& ev)
		{
			switch (ev.type)
			{
				case SDL_MOUSEMOTION:
					WorkProcMouseMove(ev);
					break;

				case SDL_MOUSEBUTTONDOWN:
					WorkProcMouseDown(ev);
					break;

				case SDL_MOUSEBUTTONUP:
					WorkProcMouseUp(ev);
					break;

				case SDL_KEYDOWN:
					WorkProcKeyDown(ev);
					break;

				case SDL_KEYUP:
					WorkProcKeyUp(ev);
					break;

				case SDL_ACTIVEEVENT:
					WorkProcActive(ev);
					break;

				case SDL_VIDEORESIZE:
					WorkProcLayout(ev);
					break;

				case SDL_QUIT:
					K_UFLAG(&_insts, QGSDI_EXIT, true);
					break;

				default:
					break;
			}
		}

		kint WorkProcMouseButton(const SDL_Event& ev, bool isdown)
		{
			switch (ev.button.button)
			{
				case SDL_BUTTON_LEFT:
					K_UFLAG(&_mouse.state, QIMM_LEFT, isdown);
					return QIM_LEFT;

				case SDL_BUTTON_RIGHT:
					K_UFLAG(&_mouse.state, QIMM_RIGHT, isdown);
					return QIM_RIGHT;

				case SDL_BUTTON_MIDDLE:
					K_UFLAG(&_mouse.state, QIMM_MIDDLE, isdown);
					return QIM_MIDDLE;

				case SDL_BUTTON_X1:
					K_UFLAG(&_mouse.state, QIMM_X1, isdown);
					return QIM_X1;

				case SDL_BUTTON_X2:
					K_UFLAG(&_mouse.state, QIMM_X2, isdown);
					return QIM_X2;

				case SDL_BUTTON_WHEELUP:
					return QIMM_WHEELUP;

				case SDL_BUTTON_WHEELDOWN:
					return QIMM_WHEELDOWN;
			}

			return 0;
		}

		void WorkProcMousePoint(const SDL_Event& ev)
		{
			_mouse.last = _mouse.point;
			_mouse.point.x = ev.motion.x;
			_mouse.point.y = ev.motion.y;
		}

		void WorkProcMouseMove(const SDL_Event& ev)
		{
			WorkProcMousePoint(ev);

			_ProcessMultiClick(0, QIMT_MOVE);

			if (CanInvokeHandler(QGSEV_MOUSEMOVE))
			{
				qgSevMouseMove e =
				{
					_mouse.point.x, _mouse.point.y,
					_mouse.point.x - _mouse.last.x, _mouse.point.y - _mouse.last.y,
					_mouse.state
				};
				UnsafeInvokeHandler(QGSEV_MOUSEMOVE, &e);
			}
		}

		void WorkProcMouseDown(const SDL_Event& ev)
		{
			kint btn = WorkProcMouseButton(ev, true);
			WorkProcMousePoint(ev);

			if (btn & (QIMM_WHEELUP | QIMM_WHEELDOWN))
			{
				// 마우스 휠
				if (CanInvokeHandler(QGSEV_MOUSEWHEEL))
				{
					qgSevMouseWheel e =
					{
						_mouse.point.x, _mouse.point.y,
						(kshort)(btn == QIMM_WHEELUP ? 1 : -1),
						_mouse.state
					};
					UnsafeInvokeHandler(QGSEV_MOUSEWHEEL, &e);
				}
			}
			else
			{
				// 증말 버튼
				bool okdbl = _ProcessMultiClick(btn, QIMT_DOWN);

				qgSevMouseButton e =
				{
					_mouse.point.x, _mouse.point.y,
					(kbyte)btn, 0,
					_mouse.state
				};

				if (!okdbl)
				{
					if (CanInvokeHandler(QGSEV_MOUSEDOWN))
						UnsafeInvokeHandler(QGSEV_MOUSEDOWN, &e);
				}
				else
				{
					e.state |= QIMM_DOUBLE;

					if (CanInvokeHandler(QGSEV_MOUSEDOWN))
						UnsafeInvokeHandler(QGSEV_MOUSEDOWN, &e);

					if (CanInvokeHandler(QGSEV_MOUSEDOUBLE))
						UnsafeInvokeHandler(QGSEV_MOUSEDOUBLE, &e);
				}
			}
		}

		void WorkProcMouseUp(const SDL_Event& ev)
		{
			kint btn = WorkProcMouseButton(ev, false);
			WorkProcMousePoint(ev);

			if ((btn & (QIMM_WHEELUP | QIMM_WHEELDOWN)) == 0)
			{
				_ProcessMultiClick(btn, QIMT_UP);

				if (CanInvokeHandler(QGSEV_MOUSEUP))
				{
					qgSevMouseButton e =
					{
						_mouse.point.x, _mouse.point.y,
						(kbyte)btn, 0,
						_mouse.state
					};
					UnsafeInvokeHandler(QGSEV_MOUSEUP, &e);
				}
			}
		}

		kushort WorkProcKeyState(kint mod)
		{
			kushort v = 0;

			if (mod&(KMOD_LSHIFT | KMOD_RSHIFT)) v |= QIS_SHIFT;
			if (mod&(KMOD_LCTRL | KMOD_RCTRL)) v |= QIS_CTRL;
			if (mod&(KMOD_LALT | KMOD_RALT)) v |= QIS_ALT;
			if (mod&KMOD_NUM) v |= QIS_NUM;
			if (mod&KMOD_CAPS) v |= QIS_CAPS;
			if (mod&KMOD_MODE) v |= QIS_SCRL;

			return _key.state = v;
		}

		void WorkProcKeyDown(const SDL_Event& ev)
		{
			qgSevKey e;

			e.key = KeyMap[ev.key.keysym.sym];
			e.extend = 0;
			e.state = WorkProcKeyState(ev.key.keysym.mod);

			switch (e.key)
			{
				case QIK_LCTRL:
				case QIK_RCTRL:
					e.extend = e.key;
					e.key = QIK_CTRL;
					break;

				case QIK_LSHIFT:
				case QIK_RSHIFT:
					e.extend = e.key;
					e.key = QIK_SHIFT;
					break;

				case QIK_LALT:
				case QIK_RALT:
					e.extend = e.key;
					e.key = QIK_ALT;
					break;
			}

			if (CanInvokeHandler(QGSEV_KEYDOWN))
				UnsafeInvokeHandler(QGSEV_KEYDOWN, &e);

			_key.key[e.key] = true;
			_key.key[e.extend] = true;

			// char
			if (CanInvokeHandler(QGSEV_CHAR))
			{
				qgSevChar e = {(kint)ev.key.keysym.unicode, _key.state};
				UnsafeInvokeHandler(QGSEV_CHAR, &e);
			}
		}

		void WorkProcKeyUp(const SDL_Event& ev)
		{
			qgSevKey e;

			e.key = KeyMap[ev.key.keysym.sym];
			e.extend = 0;
			e.state = WorkProcKeyState(ev.key.keysym.mod);

			switch (e.key)
			{
				case QIK_LCTRL:
				case QIK_RCTRL:
					e.extend = e.key;
					e.key = QIK_CTRL;
					break;

				case QIK_LSHIFT:
				case QIK_RSHIFT:
					e.extend = e.key;
					e.key = QIK_SHIFT;
					break;

				case QIK_LALT:
				case QIK_RALT:
					e.extend = e.key;
					e.key = QIK_ALT;
					break;
			}

			if (CanInvokeHandler(QGSEV_KEYUP))
				UnsafeInvokeHandler(QGSEV_KEYUP, &e);

			_key.key[e.key] = false;
			_key.key[e.extend] = false;
		}

		void WorkProcActive(const SDL_Event& ev)
		{
			bool ok =
				(ev.active.state == SDL_APPMOUSEFOCUS ||
				ev.active.state == SDL_APPINPUTFOCUS) ?
				ev.active.gain == 1 : false;

			double now = k_timer_get_abs(_timer);
			float adt = (float)(now - _active);

			_active = now;

			if (ok)
			{
				K_UFLAG(&_insts, QGSDI_ACTIVE, true);

				if (CanInvokeHandler(QGSEV_ACTIVE))
				{
					qgSevActive e = {true, adt};
					UnsafeInvokeHandler(QGSEV_ACTIVE, &e);
				}
			}
			else
			{
				K_UFLAG(&_insts, QGSDI_ACTIVE, false);

				if (CanInvokeHandler(QGSEV_ACTIVE))
				{
					qgSevActive e = {false, adt};
					UnsafeInvokeHandler(QGSEV_ACTIVE, &e);
				}

				if (!CanInvokeHandler(QGSEV_KEYUP))
					memset(_key.key, 0, sizeof(kbyte)* 256);
				else
				{
					qgSevKey e = {0, 0, 0};	// 해제기 때문에 state도 0으로 보낸다

					for (kint i = 0; i < 256; i++)
					{
						if (!_key.key[i])
							continue;

						_key.key[i] = false;
						e.key = (kbyte)i;

						UnsafeInvokeHandler(QGSEV_KEYUP, &e);
					}
				}
			}
		}

		void WorkProcLayout(const SDL_Event& ev)
		{
			tpl::Point prev(_size);

			_sysbnd.Set(0, 0, ev.resize.w, ev.resize.h);
			_bound.Set(0, 0, ev.resize.w, ev.resize.h);

			if (!K_OFLAG(flags, QGSDF_FULL))
				_size.Set(ev.resize.w, ev.resize.h);

			// 이벤트 트리거
			if (prev.x != _size.x || prev.y != _size.y)
			{
				_screen = SDL_SetVideoMode(_size.x, _size.y, 0, _sdl_flags);

				if (CanInvokeHandler(QGSEV_LAYOUT))
				{
					qgSevLayout e = {_size.x, _size.y, false};	// 최대화 상태를 알 수 없음
					UnsafeInvokeHandler(QGSEV_LAYOUT, &e);
				}
			}
		}
	};
}


//////////////////////////////////////////////////////////////////////////
// 만들기
#if 1
namespace _stub
{
	qgStub* NewStub(const tpl::Rect& area, kint flags)
	{
		_sdl::SDLStub* stub = kobj_create(_sdl::SDLStub);

		if (!stub->Create(area, flags))
		{
			stub->Unload();
			stub = NULL;
		}

		return stub;
	}

	qgStub* NewStubVirtual(kpointer handle)
	{
		// 가상을 만들 수 없다...
		// SDL.NET 이 출동한다면?
		return NULL;
	}
}
#endif

#endif
